package lab.poc.erl.showcase.timer;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import lab.poc.erl.Actor;
import lab.poc.erl.After;
import lab.poc.erl.Node;
import lab.poc.erl.Pattern;
import lab.poc.erl.Pid;

/**
 * The Timer communicates with the Clock via sending the WAKEUP message. So the
 * actual clock "action" (printing the time) runs on the clock thread (no need
 * for synchronization between the two actors).
 */
final class Clock extends Actor {

	private static final Object WAKEUP = new Object();

	public Clock() {
		// package-private actors need explicitly
		// declared public no-arg constructor
	}

	@Override
	protected void onSpawn(final List<Object> args) {
		final Long tickPeriod = (Long) args.get(0);
		int maxTicks = (Integer) args.get(1);

		@SuppressWarnings("serial")
		List<Object> timerArgs = new ArrayList<Object>() {
			{
				add(tickPeriod);
				add(new TimerCommand(self()));
			}
		};
		Pid timerPid = Node.getNode().spawnLink(PeriodicTimer.class, timerArgs);

		loop(timerPid, maxTicks);
	}

	private void loop(final Pid timerPid, final int maxTicks) {
		int ticks = 0;
		while (ticks < maxTicks) {
			receive(WakeupMessage.TICK);
			++ticks;
			System.out.println("It is " + new Date() + " now");
		}
		timerPid.send(PeriodicTimer.CANCEL);
	}

	private static final class TimerCommand implements After<Void> {
		final Pid clockPid;

		TimerCommand(final Pid clockActorPid) {
			clockPid = clockActorPid;
		}

		@Override
		public Void call() {
			clockPid.send(WAKEUP);
			return null;
		}
	}

	private static final class WakeupMessage implements Pattern {
		static final WakeupMessage TICK = new WakeupMessage();

		@Override
		public boolean approve(final Object msg) {
			return msg == WAKEUP;
		}

		@Override
		public <E extends Enum<E>> E type() { // unused
			return null;
		}
	}
}
